package org.zhq.core;

import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.channels.ReadableByteChannel;
import java.nio.channels.SocketChannel;
import java.nio.channels.WritableByteChannel;

/**
 * ByteBuffer的封装
 *
 * @author zhengquan
 * @date 2019/1/24
 */
public class IoArgs {
    private static final int DEFAULT_CAPACITY = 256;
    private volatile int limit;
    private final ByteBuffer buffer;

    private final boolean isNeedConsumeRemaining;

    public IoArgs() {
        this(DEFAULT_CAPACITY);
    }

    public IoArgs(int size) {
        this(size, true);
    }

    public IoArgs(int size, boolean isNeedConsumeRemaining) {
        this.limit = size;
        this.isNeedConsumeRemaining = isNeedConsumeRemaining;
        this.buffer = ByteBuffer.allocate(size);
    }

    public boolean isNeedConsumeRemaining() {
        return isNeedConsumeRemaining;
    }

    /**
     * 从bytes数组读取到buffer中
     *
     * @param bytes  待读入的数组bytes
     * @param offset 偏移量(指从bytes的第几位开始读入)
     * @param count  总共读入几个字节
     * @return 实际读入的字节数
     */
    public int readFrom(byte[] bytes, int offset, int count) {
        //由于buffer容量可能不够消费,因此这个需要和buffer.capacity取小
        int size = Math.min(count, buffer.remaining());
        if (size <= 0) {
            return 0;
        }
        buffer.put(bytes, offset, size);
        return size;
    }

    /**
     * 从buffer写入数据到bytes中
     *
     * @param bytes  待写入的数组bytes
     * @param offset 偏移量(指从bytes的第几位开始写入)
     * @return 实际写入的字节数
     */
    public int writeTo(byte[] bytes, int offset) {
        //buffer的剩余数据大小和bytes可写的长度
        int size = Math.min(bytes.length - offset, buffer.remaining());
        buffer.get(bytes, offset, size);
        return size;
    }

    /**
     * 从ReadableByteChannel读取数据到buffer中
     *
     * @param channel 可读取的通道
     * @return 实际读入的字节数
     * @throws IOException
     */
    @SuppressWarnings("Duplicates")
    public int readFrom(ReadableByteChannel channel) throws IOException {
        int byteProduced = 0;
        while (buffer.hasRemaining()) {
            int len = channel.read(buffer);
            if (len < 0) {
                throw new EOFException();
            }
            byteProduced += len;
        }
        return byteProduced;
    }

    /**
     * 从buffer写入数据到WritableByteChannel中
     *
     * @param channel 可写入的通道
     * @return 实际写入的字节数
     * @throws IOException
     */
    @SuppressWarnings("Duplicates")
    public int writeTo(WritableByteChannel channel) throws IOException {
        int byteProduced = 0;
        while (buffer.hasRemaining()) {
            int len = channel.write(buffer);
            if (len < 0) {
                throw new EOFException();
            }
            byteProduced += len;
        }
        return byteProduced;
    }

    /**
     * 从SocketChannel读取数据到buffer中
     *
     * @param channel 套接字通道
     * @return 实际读入的字节数
     * @throws IOException
     */
    @SuppressWarnings("Duplicates")
    public int readFrom(SocketChannel channel) throws IOException {
        ByteBuffer buffer = this.buffer;
        int byteProduced = 0;
        int len;
        do {
            len = channel.read(buffer);
            if (len < 0) {
                throw new EOFException("Channel:"+channel+"无法读取数据");
            }
            byteProduced += len;
        } while (buffer.hasRemaining() && len != 0);
        return byteProduced;
    }

    /**
     * 从buffer写入数据到SocketChannel中
     *
     * @param channel 套接字通道
     * @return 实际写入的字节数
     * @throws IOException
     */
    public int writeTo(SocketChannel channel) throws IOException {
        ByteBuffer buffer = this.buffer;
        int byteProduced = 0;
        int len;
        do {
            len = channel.write(buffer);
            if (len < 0) {
                throw new EOFException("Channel:"+channel+"无法写入数据");
            }
            byteProduced += len;
        } while (buffer.hasRemaining() && len != 0);
        return byteProduced;
    }


    /**
     * 为了复用buffer,因此要清空buffer的数据
     * 这里buffer.clear并非移除所有数据,而是将position 和limit 重置到初始位置
     */
    public void startWriting() {
        buffer.clear();
        buffer.limit(limit);
    }


    /**
     * 写入buffer完成以后,需要buffer.flip操作
     * limit赋值到position,position赋值成0
     */
    public void finishWriting() {
        buffer.flip();
    }

    /**
     * 设置单次写操作的容纳区间
     *
     * @param limit
     */
    public void limit(int limit) {
        this.limit = Math.min(limit, buffer.capacity());
    }

    public void resetLimit() {
        this.limit = buffer.capacity();
    }


    /**
     * buffer是否还有数据未操作 即position和limit的距离
     *
     * @return
     */
    public boolean remained() {
        return buffer.remaining() > 0;
    }

    /**
     * 填充空数据
     *
     * @param size 想要填充的数据长度
     * @return 实际填充的长度
     */
    public int fillEmpty(int size) {
        int fillSize = Math.min(size, buffer.remaining());
        buffer.position(buffer.position() + fillSize);
        return fillSize;
    }


    /**
     * 清空部分数据
     *
     * @param size 想要清空的数据长度
     * @return 实际清空的数据长度
     */
    public int setEmpty(int size) {
        int emptySize = Math.min(size, buffer.remaining());
        buffer.position(buffer.position() + emptySize);
        return emptySize;
    }


    /**
     * IoArgs 处理者
     */
    public interface IoArgsEventProcessor {

        /**
         * 提供一个可消费的IoArgs
         *
         * @return
         */
        IoArgs provideIoArgs();

        /**
         * 消费失败时的回调
         *
         * @param e 异常信息
         * @return 是否关闭链接 true为关闭
         */
        boolean onConsumeFailed(Throwable e);

        /**
         * 消费成功
         * @param ioArgs
         * @return true 直接注册下一份调度 false 无需注册
         */
        boolean onConsumeCompleted(IoArgs ioArgs);

    }

}
